package com.flot.yazb.widget.utils;

import org.apache.commons.codec.binary.Base64;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.Cipher;

/**
 * RSA非对称加密算法
 * 客户端公钥加密，服务器私钥解密
 * @author chongwenjun
 *
 */
public class RSAEncript {
	public static final String RSA = "RSA";// 非对称加密密钥算法
	public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式
	public static final int DEFAULT_KEY_SIZE = 2048;//秘钥默认长度
	public static final byte[] DEFAULT_SPLIT = "#PART#".getBytes();    // 当要加密的内容超过bufferSize，则采用partSplit进行分块加密
	public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;// 当前秘钥支持加密的最大字节数
	/**
	 * 默认字符集编码UTF-8
	 */
	public static final String DEFAULT_CHAR_UTF_8 = "utf-8";
	/**
	 * 签名算法
	 */
	public static final String SIGN_ALGORITHMS = "SHA1WithRSA";

	public static String PRKV="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDhRwgwwuXL96rYOI1DSmK9YP2Fw8SDeUMhKYlTvce887YY7NYP6LvrAnDgvvGSkWMKgsOoN+w4bRKOlmEvD030XHBSKllV/C9in+DaD5udAgCOEJ7FcXx6dPtadb3Fq12WgxcxinlR7/I+f8SKEffZozRMCgpQQG4IR3AMVXIBnedWRc6UA8QncGowMTFyfCBlMAmtZDd01P/3zxFXWHVyKVpf1LEKulaMX2RYaN+NKLJbfyYsat+nKea9sS2yhHqxBMpyuAzfoDMGtJFfQNq4cKVDJK66onYgUPrmqJBelBXOkUBUotwTV4CfEJ82MQmupT1gypUEA7VpxpNsW4T9AgMBAAECggEAWcO2Dcf7TRT47INMNp3zTkbRfnWNXl35U8N7RvT+G9p4muH0Qg6YooIsfEFZfO+VqWCFmgf+hlVf6ZUeUGQmydHzp6ncQxjjwMa4D+vcM+nSUslomoAn+5uVdGwqHBO2jPzbRxBIW0/jFOhQZjvWqNARTKNLjMK/lWjxythZPy7ulyIFJ8wCsZGgmz7Z2A0H76VnGAm4dwFo5D7M496EWcNd5fz6EL/8Bk978Z70fJc87+poNOJj+LFvvFrQOjfgqDTmsK/aMT0lv9Dt0Swl2upxRUKmVXQwdfyD+szhNmeyhGeYil5UVicNeYZuq70y6g6vg/HhF5Ptuoc3xeYB+QKBgQD8wlHNgcltyRNmhN9TfrHR2PLH/X0I0ert+0FRuTJ/ljbmhv72u6OV6+4K+qfUKzs+74NDHrLylWCkHHzHzmPo/dK8zGUUgrko/L5uWUXtATptVV3JkjPg/pfLHwg0MFLcl7IC7DQeqB3ZVAn7AIFTJp3OFnai85IgzBkUbQaTPwKBgQDkKoEWrd8GnTfWyCB3y1992e0UObpHWlU4TPtgQ/wKNTKqpy7bkGCkIsVcGawLhjO0e+QfcrRyhOugQIU6sDBiBDucx7BTsrnWP8Yz8VDBTABbp4w8qPdMY/axKzLWxDv/yU+JJcg3Ypq6Z1GdOmmsOci33ekoxXB9R6fJpbukwwKBgBpGpTRlAqisF503wPOwHxI+qHaL7pylHfQhg5fYVdyXpBtw+z5xIT9rqHJxvlK7L8yKH64xmc7IIqTM5VfsJ8zH3kmMQhHQySjupWlZVICf+Q/2qOpUjZodPXB7SGIMiACwsGvHXF+3gGrgZq02Tztyr0MGaMzl31IThaPu9ZkHAoGBAIzg1Z28M3WxAzZBkRKGZuqc8S5IyoSd+PpvRdLVhzyCRktqOJtnFNSjh3pre3gOH8SzrlHgkBwZiCcFT33CJc2PUYNnJK85LldkPyv5NuRy2dG9rPATX8Yx/rK7uoMy3Nz9XneO9YWFA4ZOnuzWkUK+1vj4Mqty5RbldGskQqzjAoGAA5otg5d1CpT+oS5MifO0M74gn6q2ObHI7s1NNn95MMv1KqRqYOoH8wEpgjQEK2CPZ0TK/655XR7qiGHQx08DO4P1SaTZFOQY7XxmBDuSPbThRMDi1zQTfM4x4BRLEfKZQR7EoWUKdnKKOJ+83rnVH2W4/GOrWc6AOSGVLMAHW2Q=\n";
	public static String PKV="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4UcIMMLly/eq2DiNQ0pivWD9hcPEg3lDISmJU73HvPO2GOzWD+i76wJw4L7xkpFjCoLDqDfsOG0SjpZhLw9N9FxwUipZVfwvYp/g2g+bnQIAjhCexXF8enT7WnW9xatdloMXMYp5Ue/yPn/EihH32aM0TAoKUEBuCEdwDFVyAZ3nVkXOlAPEJ3BqMDExcnwgZTAJrWQ3dNT/988RV1h1cilaX9SxCrpWjF9kWGjfjSiyW38mLGrfpynmvbEtsoR6sQTKcrgM36AzBrSRX0DauHClQySuuqJ2IFD65qiQXpQVzpFAVKLcE1eAnxCfNjEJrqU9YMqVBAO1acaTbFuE/QIDAQAB\n";
	/**
	 * 随机生成RSA密钥对
	 *
	 * @param keyLength 密钥长度，范围：512～2048
	 *                  一般1024
	 * @return
	 */
	public static KeyPair generateRSAKeyPair(int keyLength) {
		try {
			KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
			kpg.initialize(keyLength);
			return kpg.genKeyPair();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}


	/**
	 * 用公钥对字符串进行加密
	 *
	 * @param data 原文
	 */
	public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
		// 得到公钥
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
		KeyFactory kf = KeyFactory.getInstance(RSA);
		PublicKey keyPublic = kf.generatePublic(keySpec);
		// 加密数据
		Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
		cp.init(Cipher.ENCRYPT_MODE, keyPublic);
		return cp.doFinal(data);
	}

	/**
	 * 私钥加密
	 *
	 * @param data       待加密数据
	 * @param privateKey 密钥
	 * @return byte[] 加密数据
	 */
	public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception {
		// 得到私钥
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
		KeyFactory kf = KeyFactory.getInstance(RSA);
		PrivateKey keyPrivate = kf.generatePrivate(keySpec);
		// 数据加密
		Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
		cipher.init(Cipher.ENCRYPT_MODE, keyPrivate);
		return cipher.doFinal(data);
	}

	/**
	 * 公钥解密
	 *
	 * @param data      待解密数据
	 * @param publicKey 密钥
	 * @return byte[] 解密数据
	 */
	public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
		// 得到公钥
		X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
		KeyFactory kf = KeyFactory.getInstance(RSA);
		PublicKey keyPublic = kf.generatePublic(keySpec);
		// 数据解密
		Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
		cipher.init(Cipher.DECRYPT_MODE, keyPublic);
		return cipher.doFinal(data);
	}

	/**
	 * 使用私钥进行解密
	 */
	public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
		// 得到私钥
		PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
		KeyFactory kf = KeyFactory.getInstance(RSA);
		PrivateKey keyPrivate = kf.generatePrivate(keySpec);

		// 解密数据
		Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
		cp.init(Cipher.DECRYPT_MODE, keyPrivate);
		byte[] arr = cp.doFinal(encrypted);
		return arr;
	}


	/**
	 * 用公钥对字符串进行分段加密
	 *
	 */
	public static byte[] encryptByPublicKeyForSpilt(byte[] data, byte[] publicKey) throws Exception {
		int dataLen = data.length;
		if (dataLen <= DEFAULT_BUFFERSIZE) {
			return encryptByPublicKey(data, publicKey);
		}
		List<Byte> allBytes = new ArrayList<Byte>(2048);
		int bufIndex = 0;
		int subDataLoop = 0;
		byte[] buf = new byte[DEFAULT_BUFFERSIZE];
		for (int i = 0; i < dataLen; i++) {
			buf[bufIndex] = data[i];
			if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
				subDataLoop++;
				if (subDataLoop != 1) {
					for (byte b : DEFAULT_SPLIT) {
						allBytes.add(b);
					}
				}
				byte[] encryptBytes = encryptByPublicKey(buf, publicKey);
				for (byte b : encryptBytes) {
					allBytes.add(b);
				}
				bufIndex = 0;
				if (i == dataLen - 1) {
					buf = null;
				} else {
					buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
				}
			}
		}
		byte[] bytes = new byte[allBytes.size()];
		{
			int i = 0;
			for (Byte b : allBytes) {
				bytes[i++] = b.byteValue();
			}
		}
		return bytes;
	}

	/**
	 * 分段加密
	 *
	 * @param data       要加密的原始数据
	 * @param privateKey 秘钥
	 */
	public static byte[] encryptByPrivateKeyForSpilt(byte[] data, byte[] privateKey) throws Exception {
		int dataLen = data.length;
		if (dataLen <= DEFAULT_BUFFERSIZE) {
			return encryptByPrivateKey(data, privateKey);
		}
		List<Byte> allBytes = new ArrayList<Byte>(2048);
		int bufIndex = 0;
		int subDataLoop = 0;
		byte[] buf = new byte[DEFAULT_BUFFERSIZE];
		for (int i = 0; i < dataLen; i++) {
			buf[bufIndex] = data[i];
			if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
				subDataLoop++;
				if (subDataLoop != 1) {
					for (byte b : DEFAULT_SPLIT) {
						allBytes.add(b);
					}
				}
				byte[] encryptBytes = encryptByPrivateKey(buf, privateKey);
				for (byte b : encryptBytes) {
					allBytes.add(b);
				}
				bufIndex = 0;
				if (i == dataLen - 1) {
					buf = null;
				} else {
					buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
				}
			}
		}
		byte[] bytes = new byte[allBytes.size()];
		{
			int i = 0;
			for (Byte b : allBytes) {
				bytes[i++] = b.byteValue();
			}
		}
		return bytes;
	}

	/**
	 * 公钥分段解密
	 *
	 * @param encrypted 待解密数据
	 * @param publicKey 密钥
	 */
	public static byte[] decryptByPublicKeyForSpilt(byte[] encrypted, byte[] publicKey) throws Exception {
		int splitLen = DEFAULT_SPLIT.length;
		if (splitLen <= 0) {
			return decryptByPublicKey(encrypted, publicKey);
		}
		int dataLen = encrypted.length;
		List<Byte> allBytes = new ArrayList<Byte>(1024);
		int latestStartIndex = 0;
		for (int i = 0; i < dataLen; i++) {
			byte bt = encrypted[i];
			boolean isMatchSplit = false;
			if (i == dataLen - 1) {
				// 到data的最后了
				byte[] part = new byte[dataLen - latestStartIndex];
				System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
				byte[] decryptPart = decryptByPublicKey(part, publicKey);
				for (byte b : decryptPart) {
					allBytes.add(b);
				}
				latestStartIndex = i + splitLen;
				i = latestStartIndex - 1;
			} else if (bt == DEFAULT_SPLIT[0]) {
				// 这个是以split[0]开头
				if (splitLen > 1) {
					if (i + splitLen < dataLen) {
						// 没有超出data的范围
						for (int j = 1; j < splitLen; j++) {
							if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
								break;
							}
							if (j == splitLen - 1) {
								// 验证到split的最后一位，都没有break，则表明已经确认是split段
								isMatchSplit = true;
							}
						}
					}
				} else {
					// split只有一位，则已经匹配了
					isMatchSplit = true;
				}
			}
			if (isMatchSplit) {
				byte[] part = new byte[i - latestStartIndex];
				System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
				byte[] decryptPart = decryptByPublicKey(part, publicKey);
				for (byte b : decryptPart) {
					allBytes.add(b);
				}
				latestStartIndex = i + splitLen;
				i = latestStartIndex - 1;
			}
		}
		byte[] bytes = new byte[allBytes.size()];
		{
			int i = 0;
			for (Byte b : allBytes) {
				bytes[i++] = b.byteValue();
			}
		}
		return bytes;
	}

	/**
	 * 使用私钥分段解密
	 *
	 */
	public static byte[] decryptByPrivateKeyForSpilt(byte[] encrypted, byte[] privateKey) throws Exception {
		int splitLen = DEFAULT_SPLIT.length;
		if (splitLen <= 0) {
			return decryptByPrivateKey(encrypted, privateKey);
		}
		int dataLen = encrypted.length;
		List<Byte> allBytes = new ArrayList<Byte>(1024);
		int latestStartIndex = 0;
		for (int i = 0; i < dataLen; i++) {
			byte bt = encrypted[i];
			boolean isMatchSplit = false;
			if (i == dataLen - 1) {
				// 到data的最后了
				byte[] part = new byte[dataLen - latestStartIndex];
				System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
				byte[] decryptPart = decryptByPrivateKey(part, privateKey);
				for (byte b : decryptPart) {
					allBytes.add(b);
				}
				latestStartIndex = i + splitLen;
				i = latestStartIndex - 1;
			} else if (bt == DEFAULT_SPLIT[0]) {
				// 这个是以split[0]开头
				if (splitLen > 1) {
					if (i + splitLen < dataLen) {
						// 没有超出data的范围
						for (int j = 1; j < splitLen; j++) {
							if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
								break;
							}
							if (j == splitLen - 1) {
								// 验证到split的最后一位，都没有break，则表明已经确认是split段
								isMatchSplit = true;
							}
						}
					}
				} else {
					// split只有一位，则已经匹配了
					isMatchSplit = true;
				}
			}
			if (isMatchSplit) {
				byte[] part = new byte[i - latestStartIndex];
				System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
				byte[] decryptPart = decryptByPrivateKey(part, privateKey);
				for (byte b : decryptPart) {
					allBytes.add(b);
				}
				latestStartIndex = i + splitLen;
				i = latestStartIndex - 1;
			}
		}
		byte[] bytes = new byte[allBytes.size()];
		{
			int i = 0;
			for (Byte b : allBytes) {
				bytes[i++] = b.byteValue();
			}
		}
		return bytes;
	}

	/**
	 * 从字符串中加载公钥
	 *
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @retrun RSAPublicKey
	 */
	public static RSAPublicKey loadPublicKeyByStr(String publicKeyStr) {
		try {
			byte[] buffer = Base64.decodeBase64(publicKeyStr);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			return (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 从字符串中加载私钥
	 *
	 * @param privateKeyStr
	 *            私钥数据字符串
	 * @retrun RSAPrivateKey
	 */
	public static RSAPrivateKey loadPrivateKeyByStr(String privateKeyStr) {
		try {
			byte[] buffer = Base64.decodeBase64(privateKeyStr);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * RSA验签名检查(公钥签名检查)
	 *
	 * signByPrivateKey方法对应doCheckByPublicKey方法
	 *
	 * @param content
	 *            待签名数据
	 * @param sign
	 *            签名数据
	 * @param publicKey
	 *            公钥
	 * @param encode
	 *            字符集编码
	 * @return 布尔值
	 */
	public static boolean doCheckByPublicKey(String content, String sign, String publicKey,
											 String encode) {
		try {
//			Cipher CheckCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			byte[] encodedKey = Base64.decodeBase64(publicKey);
			PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));

			java.security.Signature signature = java.security.Signature
					.getInstance(SIGN_ALGORITHMS);

			signature.initVerify(pubKey);

			if (StringUtils.isEmpty(encode) || StringUtils.isEmpty(encode.trim())) {
				encode = DEFAULT_CHAR_UTF_8;
			}
			signature.update(content.getBytes(encode));

			boolean bverify = signature.verify(Base64.decodeBase64(sign));
			return bverify;

		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}


	/**
	 * RSA签名(私钥签名)
	 *
	 * signByPrivateKey方法对应doCheckByPublicKey方法
	 *
	 * @param content
	 *            待签名数据
	 * @param privateKey
	 *            私钥
	 * @param encode
	 *            字符集编码
	 * @return 签名值
	 */
	public static String signByPrivateKey(String content, String privateKey, String encode) {
		try {
			PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));

			KeyFactory keyf = KeyFactory.getInstance("RSA");
			PrivateKey priKey = keyf.generatePrivate(priPKCS8);

			java.security.Signature signature = java.security.Signature
					.getInstance(SIGN_ALGORITHMS);

			signature.initSign(priKey);

			if (StringUtils.isEmpty(encode) || StringUtils.isEmpty(encode.trim())) {
				encode = DEFAULT_CHAR_UTF_8;
			}
			signature.update(content.getBytes(encode));

			byte[] signed = signature.sign();

			return Base64.encodeBase64String(signed);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public static void main(String[] args) {
		try {
			String jsonData = "-=+~O(∩_∩)O哈哈~Hello+=-?><,./;':\"\\_)(0987654321`~!@#$%^&*()_+	|";
			KeyPair keyPair=generateRSAKeyPair(DEFAULT_KEY_SIZE);
			// 公钥
//			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
			RSAPublicKey publicKey = loadPublicKeyByStr(PKV);
//			String pk=new String(Base64.encodeBase64(publicKey.getEncoded()));
//			System.out.println("公钥---->"+pk);
			// 私钥
//			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
			RSAPrivateKey privateKey = loadPrivateKeyByStr(PRKV);
//			String rk=new String(Base64.encodeBase64(privateKey.getEncoded()));
//			System.out.println("私钥---->"+rk);

			System.out.println(
					"验证sign数据结果: " + doCheckByPublicKey(jsonData, signByPrivateKey(jsonData, PRKV, ""),PKV, ""));


			//公钥加密
			long start=System.currentTimeMillis();
			byte[] encryptBytes= encryptByPublicKeyForSpilt(jsonData.getBytes(),publicKey.getEncoded());
			long end=System.currentTimeMillis();
			System.out.println("公钥加密耗时 cost time---->"+(end-start));
			String encryStr= Base64Utils.encode(encryptBytes);
			System.out.println("加密后json数据 -->"+encryStr);
			System.out.println("加密后json数据长度 -->"+encryStr.length());
			//私钥解密
			start=System.currentTimeMillis();
			byte[] decryptBytes= decryptByPrivateKeyForSpilt(Base64Utils.decodePrivate(encryStr),privateKey.getEncoded());
			String decryStr=new String(decryptBytes);
			end=System.currentTimeMillis();
			System.out.println("私钥解密耗时 cost time---->"+(end-start));
			System.out.println("解密后json数据 --1-->"+decryStr);

			//私钥加密
			start=System.currentTimeMillis();
			encryptBytes= encryptByPrivateKeyForSpilt(jsonData.getBytes(),privateKey.getEncoded());
			end=System.currentTimeMillis();
			System.out.println("私钥加密密耗时 cost time---->"+(end-start));
			encryStr=Base64Utils.encode(encryptBytes);
			System.out.println("加密后json数据 --2-->"+encryStr);
			System.out.println("加密后json数据长度 --2-->"+encryStr.length());
			//公钥解密
			start=System.currentTimeMillis();
			decryptBytes= decryptByPublicKeyForSpilt(Base64Utils.decodePrivate(encryStr),publicKey.getEncoded());
			decryStr=new String(decryptBytes);
			end=System.currentTimeMillis();
			System.out.println("公钥解密耗时 cost time---->"+(end-start));
			System.out.println("解密后json数据 --2-->"+decryStr);
		}catch (Exception e){

		}

	}
}